/*
 * MOSGateDescription.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */
package engine;

import java.util.*;
import java.io.*;
import middle.*;

/**
 * Mos gates:
 * [r]cmos, [r]nmos, [r]pmos
 */

class MOSGateDescription extends StdGateDescription
{

	MOSGateDescription (byte type) {
		super(type, 3);
	}

	///////////////////////////////
	// ModuleFactory implementation
	///////////////////////////////

	/*
	 * this one actally is a shamelessly made copy of NInputGateDescription
	 * WARNING: strength is ignored, but specifying it will not cause any error
	 * to be reported.
	 */
	public Object createNewModule(NameSpace parent,
			ModuleInstanceDescription miDesc,
			byte strength,
			ScopeNode scope)
	throws ParseException
	{
		xConsole.debug("cerating new MOSGate");
		ExpressionDescription output;
		Enumeration e = miDesc.portsVector.elements();
		output = (ExpressionDescription) e.nextElement();
		if (output == null) {
			xConsole.debug("MOSGate not created: output is null");
			return null; //no output, so ..
		}
		try {
			try {
				((AssignableSelection)output).toContSelection(strength);
			} catch (ClassCastException cex) {
				throw new ParseException ("output expression is not" +
				" a net selection or concatenation");
			}

			SelectionExpressionDescription input = new
			SelectionExpressionDescription();
			ExpressionDescription data_in;

			if (e.hasMoreElements()) { //the input port
				ExpressionDescription ck = (ExpressionDescription) e.nextElement();
				input.addChunk(data_in = (ck == null ? ConstantDescription.zDescription :
					ck));
			} else
				throw new ParseException("wrong number of ports");

			if (e.hasMoreElements()) {//the enable port (or the ncontrol for coms)
				ExpressionDescription ck = (ExpressionDescription) e.nextElement();
				input.addChunk(ck == null ? ConstantDescription.zDescription :
					ck);
			} else
				throw new ParseException("wrong number of ports");

			if (type == cmos || type == rcmos) {
				SelectionExpressionDescription p_inputs = new
				SelectionExpressionDescription();
				p_inputs.addChunk(data_in);

				if (e.hasMoreElements()) {//pcontrol_terminal
					ExpressionDescription ck = (ExpressionDescription) e.nextElement();
					p_inputs.addChunk(ck == null ? ConstantDescription.zDescription :
						ck);
				} else
					throw new ParseException("wrong number of ports");
				if(e.hasMoreElements())
					throw new ParseException(" wrong number of ports");
				MOSGate mg[] = new MOSGate[2];
				mg[0] = new MOSGate(parent, delay,
						(LeftValue) output.instantiate(parent),
						input.instantiate(parent),
						miDesc, type == cmos ? nmos : rnmos);
				mg[0] = new MOSGate(parent, delay,
						(LeftValue) output.instantiate(parent),
						p_inputs.instantiate(parent),
						miDesc, type == cmos ? pmos : rpmos);
				return mg;
			} else {
				if(e.hasMoreElements())
					throw new ParseException("wrong number of ports");
				return new MOSGate (parent, delay,
						(LeftValue) output.instantiate(parent),
						input.instantiate(parent),
						miDesc, type);
			}
		} catch (ParseException pex) {
			throw new ParseException (parent.desc.toString(miDesc.lineNo) +
					": error: " + pex);
		}

	}

	/**
	 * The difference from NInputGateDescription's createNew....Array
	 * is that the intarmediary structure for bundling outputs is a
	 * Wire.
	 */
	public Object createNewModuleArray(NameSpace parent,
			ModuleInstanceDescription miDesc,
			byte strength,
			ScopeNode scope, int ars, int are,
			int n, int increment)
	throws ParseException
	{
		Enumeration en = miDesc.portsVector.elements();
		try {
			if (!en.hasMoreElements())
				throw new ParseException("wrong number of ports");

			ExpressionDescription output = (ExpressionDescription) en.nextElement();
			if (output == null) return null; //no output, so ..

			MOSGate gates[] = new MOSGate[n];
			Vector[] inputs  = new Vector[n]; //the input chunks
			for (int i = 0; i < n; i++)
				inputs[i] = new Vector();
			LeftValue[] outputs = new LeftValue[n];
			Monitor m;

			boolean isCmos = (type == cmos) || (type == rcmos);
			/*
			 * if this is a cmos, the we need a pair of inputs/outputs fo the
			 * pmos part of it
			 */
			LeftValue[] pmosOutputs = null;

			if (isCmos)
				pmosOutputs = new LeftValue[n];

			try {
				((AssignableSelection) output).toContSelection(strength);
			} catch (ClassCastException cex) {
				throw new ParseException("bad output port expression: not a net");
			}

			LeftValue out;
			try {
				out = (LeftValue) output.instantiate(parent);
			} catch (ClassCastException cex) {
				throw new ParseException ("bad output port expression: not a lValue");
			}

			if (((Expression) out).length != 1) {
				if (((Expression) out).length != n)
					throw new ParseException ("bad output port length");


				Wire buffer = new Wire (null, null, Assignable.typeWire,
						out.length()-1, 0,
						WireDescription.defaultExpandType,
						Delay3.nullDelay());
				for (int i = n-1; i >= 0 ; i--)
					outputs[i] = new ContBitSelect(parent, buffer, null, i, i);
				if (isCmos)
					for (int i = n-1; i >= 0 ; i--)
						pmosOutputs[i] = new ContBitSelect(parent, buffer, null, i, i);
				m = MContAssignMonitor.
				createNewMContAssignMonitor(out, new BitSelect(parent, buffer,
						null, n-1, 0)); 
			} else {
				outputs[0] = out;
				for (int i = 1; i < n; i++)
					outputs[i] = (LeftValue) output.instantiate(parent);
				if (isCmos) {
					for (int i = 0; i < n; i++)
						pmosOutputs[i] = (LeftValue) output.instantiate(parent);
				}
			}

			if (isCmos) {
				Vector[] p_inputs = new Vector[n];
				if (!en.hasMoreElements())
					throw new ParseException("wrong number of ports");
				ExpressionDescription ed =
					(ExpressionDescription) en.nextElement();
				createInputLink(parent, ed, inputs, n);
				for(int i = 0; i < n; i++)
					p_inputs[i] = (Vector) inputs[i].clone();

				if (!en.hasMoreElements())
					throw new ParseException("wrong number of ports");
				ed =
					(ExpressionDescription) en.nextElement();
				createControlLink(parent, ed, inputs, n);

				if (!en.hasMoreElements())
					throw new ParseException("wrong number of ports");
				ed =
					(ExpressionDescription) en.nextElement();
				createControlLink(parent, ed, p_inputs, n);

				MOSGate mg[][] = new MOSGate[2][n];
				for(int i = 0; i < n; i++) {
					mg[0][i] = new MOSGate(parent, delay, outputs[i],
							new SelectionExpression (parent,
									inputs[i],
									-8778),
									miDesc, type == cmos ? nmos : rnmos);
					mg[1][i] = new MOSGate(parent, delay, pmosOutputs[i],
							new SelectionExpression (parent,
									p_inputs[i],
									-8778),
									miDesc, type == cmos ? pmos : rpmos);
				}

				return mg;
			} else {
				if (!en.hasMoreElements())
					throw new ParseException("wrong number of ports");
				ExpressionDescription ed =
					(ExpressionDescription) en.nextElement();
				createInputLink(parent, ed, inputs, n);

				if (!en.hasMoreElements())
					throw new ParseException(": error: wrong number of ports");
				ed =
					(ExpressionDescription) en.nextElement();
				createControlLink(parent, ed, inputs, n);

				if (en.hasMoreElements())
					throw new ParseException(parent.desc.toString(miDesc.lineNo) +
							": error: wrong number of ports");

				for (int i = 0; i < n; i++) {
					gates[i] = new MOSGate(parent, delay, outputs[i],
							new SelectionExpression (parent,
									inputs[i],
									-8778),
									miDesc, type);
				}

				return gates;
			}
		} catch (ParseException pex) {
			throw new ParseException(parent.desc.toString(miDesc.lineNo) +
					": error: " + pex);
		}
	}

	void createInputLink(NameSpace parent, ExpressionDescription ed,
			Vector[] inputs, int n) throws ParseException{

		Expression e =
			ed == null ? ConstantDescription.zDescription.instantiate(parent) :
				ed.instantiate(parent);
			Monitor m;
			if (e.length != 1) {
				if (e.length != n)
					throw new ParseException("bad input port expression" +
							" length for \"" + ed + "\"");
				Wire buffer = new Wire (null, null, Assignable.typeWire,
						e.length-1, 0,
						WireDescription.defaultExpandType,
						Delay3.nullDelay());
				m = MContAssignMonitor.
				createNewMContAssignMonitor(new ContBitSelect(parent, buffer,
						null, n-1, 0),
						e);
				for (int i = n-1; i >= 0; i--)
					inputs[i].addElement(new BitSelect(parent, buffer, null, i, i));
			} else
				for (int i = 0; i < n; i++)
					inputs[i].addElement(e);
	}

	void createControlLink(NameSpace parent, ExpressionDescription ed,
			Vector[] inputs, int n)
	throws ParseException
	{
		Monitor m;
		Expression e =
			ed == null ? ConstantDescription.zDescription.instantiate(parent) :
				ed.instantiate(parent);

			if (e.length != 1) {
				if (e.length != n)
					throw new ParseException("bad input port expression" +
							" length for \"" + ed + "\"");
				BitVector buffer = new BitVector (e.length-1, 0, false);
				m = new LinkMonitor(new BitSelect(parent, buffer,
						null, n-1, 0),
						e);
				for (int i = n-1; i >= 0; i--)
					inputs[i].addElement(new BitSelect(parent, buffer, null, i, i));
			} else
				for (int i = 0; i < n; i++)
					inputs[i].addElement(e);
	}
}







